package com.seer.leshantools.server;

import com.google.gson.Gson;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;

public class MessageServerImpl implements MessageServer {

    LeshanServer server;
    Gson gson = new Gson();
    protected int sendId;
    protected int resultId;
    protected int objectId;

    public static MessageServer getMessageServer(LeshanServer server, int sendId, int resultId, int objectId) {
        MessageServerImpl s = new MessageServerImpl();
        s.server = server;
        server.getObservationService().addListener(new ReturnAdapter(s, s.gson));
        s.sendId = sendId;
        s.resultId = resultId;
        s.objectId = objectId;
        return s;
    }

    protected class MessageEvent {
        String uuid;
        Message response;
        CountDownLatch countDownLatch;
        DoMessage doMessage;

        public MessageEvent(String uuid,  DoMessage doMessage) {
            this.uuid = uuid;
            this.doMessage = doMessage;
        }

        public void await() throws Exception {
            this.countDownLatch = new CountDownLatch(1);
            this.countDownLatch.await();
        }

        public boolean isSync() {
            return this.countDownLatch != null;
        }

        public void awake() {
            this.countDownLatch.countDown();
        }
    }

    private Map<String, MessageEvent> cache = new ConcurrentHashMap<>();

    @Override
    public void response(Message response) throws Exception {
        if (cache.containsKey(response.getUuid())) {
            MessageEvent messageEvent = cache.get(response.getUuid());
            messageEvent.response = response;
            if (messageEvent.isSync()) {
                messageEvent.awake();
                return;
            }
            if (messageEvent.doMessage != null) {
                messageEvent.doMessage.afterReceive(messageEvent);
            }
            return;
        }
        throw new Exception("Request is not existed or time out");
    }

    @Override
    public Message syncRequest(Message request)  {
        DoMessage dm = new DoMessage() {
            @Override
            public void beforeSend(MessageEvent e) {
            }
            @Override
            public void afterSend(MessageEvent e) {
                try {
                    e.await();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            @Override
            public void afterReceive(MessageEvent e) {
            }
        };
        Message m = send(request, dm);
        return m;
    }

    @Override
    public void asyncRequest(Message request, DoMessage doMessage) {
        send(request, doMessage);
    }

    public Message send(Message request, DoMessage doMessage) {
        WriteRequest req = new WriteRequest(this.objectId, 0, this.sendId, request.toJson(this.gson));
        MessageEvent e = new MessageEvent(request.getUuid(), null);
        this.cache.put(e.uuid, e);
        if (doMessage != null) {
            doMessage.beforeSend(e);
        }
        try {
            server.send(request.getRegistration(), req);
            if (doMessage != null) {
                doMessage.afterSend(e);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return e.response;
    }
}
